/* Dancing Links DLX重复覆盖问题
* 1.DS 十字链表

* 2.题型
    1.精确覆盖问题
    2.配合覆盖问题(配合IDA*)

* 3.重复覆盖问题 IDEA*->迭代加深 估价函数
    dfs()
    1.建立十字链表后首先选择未被选中的含1最少的列，
    
    2.然后在列中选择行，由于该行已经被选中了，则该行中所有含有1的列也应该删除，
    即对于行中所有含1的那一列中所有在左右方向的节点都要删除，
    注意该行中在左右方向的节点不能删除，一方面这些节点不会被遍历到，另一方面还需要恢复，
    
    3.最后关键的地方在于估计函数的设计：
        找出所有没被覆盖的列，
        对于某一列来说，其至少要从里面选择一行，但我们可以选择将所有的行全部选中，
        但是代价却只有一行的代价，最后的总代价肯定是不超过真实代价的，
        即如果当前选择的函数加上估计函数要大于枚举的答案的话肯定不行

        cnt=0;
        遍历当前未被覆盖的列
            选择当前列中的所有行
                cnt++;
*/

#include <cstdio>
#include <cstring>
using namespace std;
#define DEBUG
inline int read()
{
	int x=0;char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar();
	return x;
}
inline void write(int x)
{
	if (x>=10) write(x/10);
	putchar(x%10+48);
}//快读快输

const int N=10010, INF=0x3f3f3f3f;

int n,m;
int l[N], r[N], u[N], d[N], s[N], row[N], col[N], idx; 
//左右上下指针            每列1的数量 每个节点所在行，列 节点编号
int ans[N], top; //最终选择行 ans指针
bool st[110];

void init() //初始化循环链表指针
{
    for(int i=0; i<=m; i++)
    {
        l[i]=i-1, r[i]=i+1;
        u[i]=d[i]=i; //只有一行，上下指针指向自己
    }
    l[0]=m, r[m]=0; //左右边界
    idx=m+1;
}

//添加节点(x, y) 列头(->idx(x,y)->...
void add(int &hh, int &tt, int x, int y) //行的头尾节点索引 新节点的位置 y是列号，也是这个列链表的头节点
{
    row[idx]=x, col[idx]=y, s[y]++; //记录当前节点位置(设置为横向，纵向的列头) 更新
    u[idx]=y, d[idx]=d[y], u[d[y]]=idx, d[y]=idx; //插入列y纵向链表 y->idx->...
    //新节点的上个节点是队头y 新节点的下个节点是原队头的下个节点 原下个节点的上个节点是新节点
    r[hh]=l[tt]=idx, r[idx]=tt, l[idx]=hh, tt=idx++; //横向链表队尾
} 

int h() //启发函数 估计当前状态下还需要选择多少行才能完成精确覆盖
{
    int cnt=0;
    memset(st, 0, sizeof st);
    for(int i=r[0]; i; i=r[i])
    {
        if(st[col[i]]) continue;
        cnt++;
        st[col[i]]=true;
        for(int j=d[i]; j!=i; j=d[j])
            for(int k=r[j]; k!=j; k=r[k]) st[col[k]]=true;
    }
    return cnt;
}

void remove(int pos) //删除pos列
{
    for(int i=d[pos]; i!=pos; i=d[i]) //重复覆盖不删列头
        l[r[i]]=l[i], r[l[i]]=r[i];
}

void resume(int pos) //恢复pos列 与remove逆操作，访问顺序相反
{
    for(int i=u[pos]; i!=pos; i=u[i])
        l[r[i]]=i, r[l[i]]=i;
}

bool dfs(int k, int depth) //迭代加深
{
    if(k+h()>depth) return false; 
    if(!r[0]) return true;

    //找到1的个数最少的列
    int pos=r[0];
    for(int i=r[0]; i; i=r[i])
        if(s[i]<s[pos])
            pos=i;

    for(int i=d[pos]; i!=pos; i=d[i]) //枚举当前选择行
    {
        ans[k]=row[i];
        remove(i);
        for(int j=r[i]; j!=i; j=r[j]) remove(j); //当前行中的其他1所在列删除
        if(dfs(k+1, depth)) return true;
        //无解，恢复现场
        for(int j=l[i]; j!=i; j=l[j]) resume(j); //恢复当前行
        resume(i);
    }
    return false;
}

signed solve()
{
    n=read(), m=read();
    init();
    for(int i=1; i<=n; i++) //插入第i行
    {
        int hh=idx, tt=idx;
        for(int j=1; j<=m; j++)
        {
            int x=read();
            if(x) add(hh, tt, i, j);
        }
    }

    int depth=0;
    while(!dfs(0, depth)) ++depth;
    write(depth), puts("");
    for(int i=0; i<depth; i++) write(ans[i]), putchar(' ');

    puts("");
    return 0;
}


signed main()
{
    #ifdef DEBUG
        freopen("./in.txt","r",stdin);
        freopen("./out.txt","w",stdout);
    #endif

    
    int T=1; //T=read();
    while(T--)
    {
        solve();
    }
    return 0;
}